package client;

import handler.ClientDefaultHandler;
import handler.ClientHandler;
import handler.PipelineDefaultHandler;
import handler.PipelineHandler;
import handler.heartbeat.HeartBeatHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class SwordClient {

	private volatile ChannelFuture channelFuture;

	public ChannelFuture getChannelFuture() {
		while (channelFuture == null) Thread.onSpinWait();
		return channelFuture;
	};


	public void run(String host, int port, PipelineHandler pipelineHandler) {
		Thread thread = Thread.ofPlatform().factory().newThread(()->{
			start(host, port, pipelineHandler,new ClientDefaultHandler());
		});
		thread.start();

	}

	public void heartBeatRun(String host, int port, HeartBeatHandler heartBeatHandler) {
		Thread thread = Thread.ofPlatform().factory().newThread(()->{
			start(host, port, pipeline -> {
				pipeline.addLast(new StringDecoder());
				pipeline.addLast(new StringEncoder());
			},new ClientDefaultHandler());
		});
		thread.start();
		Thread.ofPlatform().factory().newThread(()->{
			try {
				heartBeatHandler.handle(getChannelFuture());
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
		}).start();
	}


	private void start(String host, int port, PipelineHandler pipelineHandler, ClientHandler clientHandler) {
		try (EventLoopGroup workerGroup = new NioEventLoopGroup()){
			Bootstrap bootstrap = new Bootstrap();
			bootstrap.group(workerGroup);
			bootstrap.channel(NioSocketChannel.class);
			bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
			bootstrap.handler(new ChannelInitializer<SocketChannel>() {
				@Override
				public void initChannel(SocketChannel ch) {
					ChannelPipeline pipeline = ch.pipeline();
					pipelineHandler.handle(pipeline);
				}
			});

			channelFuture = bootstrap.connect(host, port).sync();

			clientHandler.handle(channelFuture);

			// Wait until the connection is closed.
			channelFuture.channel().closeFuture().sync();// 会阻塞

		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
	}

}
